/*
    select 模型实现网络通讯的服务端

    水平触发
        select()监听的socket如果发生了事件，select()会返回
            如果时间没有被处理，再次调用select()回立即再通知

    存在问题
        采用轮询方式扫描bitmap，性能会随着socket数量增加而下降
        每次调用select(),需要拷贝bitmap
        bitmap的大小，由FD_SETSIZE宏设置，默认1024
*/
#include <iostream>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>

// 初始化服务端的监听端口
int initserver(int port);

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        std::cout << "Usgae:./tcpselect port" <<std::endl;
        return -1;
    }
    
    // 初始化服务端用于监听的socket
    int listen_sock = initserver(std::atoi(argv[1]));
    std::cout << "listen_sock=" << listen_sock <<std::endl;

    if (listen_sock < 0)
    {
        std::cout << "initserver() failed" <<std::endl;
        return -1;
    }
    /*
        读事件
            1、已连接队列中有已经准备好的socket（有新的客户端连上来了）
            2、接收缓存中有数据可以读（对端发送的报文已到达）
            3、tcp连接一下已断开（对端调用close函数关闭了连接）
        写事件
            发送缓冲区没有满，可以写入数据（可以向对端发送报文）
    */
    
    // 需要监听读事件的socket集合，大小为16字节(1024) 的bitmap
    fd_set readfds;
    // 初始化readfds，吧bitmap的每一位都置为0
    FD_ZERO(&readfds);
    // 把服务端用于监听的socket加入readfds
    FD_SET(listen_sock, &readfds);

    // readfds中socket的最大值
    int max_fd = listen_sock;

    while (true)
    {
        // 用于表示超时事件的结构体
        struct timeval timeout;
        timeout.tv_sec = 10; 
        timeout.tv_usec = 0;    // 微秒

        // 在select()中，会修改bitmap，故要把readfds复制一份给tmpfds，再把tmpfds传给select
        fd_set tmpfds = readfds;
        // select()等待时间的发生，监听哪些socket发生了事件   select 第一个参数填要监听的bitmap有多大
        // select 参数 读事件，写事件，异常事件 下例中只监听读事件，写事件和异常事件填nullptr
        int infds = select(max_fd+1, &tmpfds, nullptr, nullptr, &timeout); 

        if (infds < 0)
        {
            // 调用select失败
            perror("select() failed");
            break;
        }
        
        if (infds == 0)
        {
            // select() 超时
            perror("select() timeout");
            continue;
        }
        
        // 如果infds>0 表示有事件发生，infds存放了发生事件的个数
        for (int event_fd = 0; event_fd <= max_fd; event_fd++)
        {
            if (FD_ISSET(event_fd, &tmpfds) == 0)
            {
                // 如果event_fd在bitmap中标志为0，表示没有事件
                continue;
            }
            
            if (event_fd == listen_sock)
            {
                // 如果发生事件的是listensock 表示已连接队列中有已准备好的socket(有新的客户端连上来了)
                struct sockaddr_in client;
                socklen_t len = sizeof(client);
                int client_sock = accept(listen_sock, (struct sockaddr *)&client, &len);
                if (client_sock < 0)
                {
                    perror("accept failed");
                    continue;
                }
                std::cout << "accept client(socket=)" << client_sock <<std::endl;

                // 把bitmap中新连上来的客户端标志 置为 1
                FD_SET(client_sock, &readfds);

                if (max_fd < client_sock)
                {
                    // 更新max_fd值
                    max_fd = client_sock;
                }
            }
            else
            {
                // 如果是客户端连接的socket有事件，表示接受缓存中有数据可以读(对端发送的报文已到达)，或者有客户端已断开连接
                char buffer[1024];
                memset(buffer, 0, sizeof(buffer));
                if (recv(event_fd, buffer, sizeof(buffer), 0) <= 0)
                {
                    // 如果客户端的连接已断开
                    std::cout << "client(event_fd="  << event_fd << ") disconnected" <<std::endl;
                    // 关闭客户端的socket
                    close(event_fd);

                    // 把bitmap中已关闭客户端的标志位清空
                    FD_CLR(event_fd, &readfds);

                    if (event_fd == max_fd)
                    {
                        // 更新max_fd的值，只有当event_fd == max_fd时才更新
                        for (int i = max_fd; i>0; i--)
                        {
                            if (FD_ISSET(i, &readfds))  
                            {
                                // 当前bit在bitmap中被设置
                                max_fd = i;
                                break;
                            }   
                        }   
                    }
                }
                else
                {
                    std::cout << "recv(event_fd=" << event_fd << ":" << buffer <<std::endl;
                    send(event_fd, buffer, strlen(buffer), 0);  // 将接受到的消息原发送回去
                }
            }
        }
    }
    return 0;
}


int initserver(int port)
{
    int sock = socket(AF_INET, SOCK_STREAM, 0);

    if (sock < 0)
    {
        perror("socket() failed");
        return -1;
    }
    
    int opt = 1;
    unsigned int len = sizeof(opt);
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, len);

    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(port);

    if (bind(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
    {
        perror("bind() failed");
        return -1;
    }
    
    if (listen(sock, 5) != 0)
    {
        perror("listen() failed");
        return -1;
    }
    return sock;
}
